package com.lmwyc.core;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lmwyc.bean.Area;
import com.lmwyc.bean.Card;
import com.lmwyc.bean.CardList;
import com.lmwyc.bean.ExtDayConfig;
import com.lmwyc.bean.UserSession;
import com.lmwyc.constant.Constant;
import com.lmwyc.exception.DistanceException;
import com.lmwyc.exception.InitAreaException;
import com.lmwyc.mapper.ExtDayConfigMapper;
import com.lmwyc.service.IAreaService;
import com.lmwyc.util.HttpUtil;
import com.lmwyc.util.MailUtil;
import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.type.PhoneNumber;

public class PunshAllThread implements Runnable {
	private static final Logger logger = LoggerFactory.getLogger(PunshAllThread.class);
	private boolean isSucced = false;

	private UserSession user;
	private MailUtil mail;
	private String punshFailedTitle;
	private String punshFailedContent = "";
	
	private ExtDayConfigMapper extDayConfigMapper;
	
	private IAreaService areaService;

	/**
	 * 区域列表
	 */
	private List<Area> areas;
	/**
	 * 打卡经度
	 */
	private double latitude;
	/**
	 * 打卡纬度
	 */
	private double longitude;

	public void setAreas(List<Area> areas) {
		this.areas = areas;
	}

	
	public void setExtDayConfigMapper(ExtDayConfigMapper extDayConfigMapper) {
		this.extDayConfigMapper = extDayConfigMapper;
	}
	
	public void setAreaService(IAreaService areaService) {
		this.areaService = areaService;
	}

	public PunshAllThread(UserSession user, MailUtil mail, String punshFailedTitle, String punshFailedContent) {
		super();
		this.user = user;
		this.mail = mail;
		this.punshFailedTitle = punshFailedTitle;
		this.punshFailedContent = punshFailedContent;
	}

	@Override
	public void run() {
		// 判断是否有特殊处理，例如周末配置为需要打卡，工作日配置为不打卡
		// 首先取个人的独立配置
		ExtDayConfig dayConfig = extDayConfigMapper.selectOne(this.user.getId());
		// 如果没有个人独立配置，再取全体默认配置
		if (dayConfig == null) {
			dayConfig = extDayConfigMapper.selectOne(-1);
		}
		if (dayConfig != null) {// 如果有特殊配置
			if (dayConfig.getIsPunsh() == 0) {// 如果特殊配置为不打卡，不管周几，直接结束进程
				logger.info(user.getUserName() + " 特殊配置不打卡");
				mail.sendSimpleMail(user.getEmail(), "自动打卡", "特殊配置，无需自动打卡");
				return;
			} else if (dayConfig.getIsPunsh() == 1) {// 如果特殊配置为打卡，不管周几，继续打卡进程
				execAuto();
			}
		} else {// 如果没有特殊配置，自行判断周末不打卡
			Calendar c = Calendar.getInstance();
			if (c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || c.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {// 周末不打卡
				return;
			} else {// 否者执行打卡
				execAuto();
			}
		}
	}
	
	/**
	 * 正式执行程序-自动
	 */
	private void execAuto() {
		try {
			// 随机休眠一定时间
			int delay = user.getRandomDelay();
			if (delay > 0) {
				delay = new Random().nextInt(delay * 60);
				logger.info(user.getUserName() + " 打卡延迟 " + (delay / 60.0) + "分钟");
				Thread.sleep(delay * 1000);
			}

			// 已打卡直接结束
			if (checkPunshed(user)) {
				logger.info(user.getUserName() + " 检测到已打卡，无需自动打卡");
				mail.sendSimpleMail(user.getEmail(), "自动打卡", "检测到已打卡，无需自动打卡");
				return;
			}
			// 第一步，初始化区域数据
			initAreaData();

			// 第二步，执行打卡，完全按微信流程执行
			getMylocation();
		} catch (IOException e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		} catch (InitAreaException e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		} catch (InterruptedException e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		} catch (DistanceException e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		}
		if (isSucced) {
			StringBuffer content = new StringBuffer();
			content.append("<html>");
			content.append("<style>");
			content.append("table{border-right:2px solid #000000;border-bottom:2px solid #000000}");
			content.append("table td{border-left:2px solid #000000;border-top:2px solid #000000}");
			content.append("table th{border-left:2px solid #000000;border-top:2px solid #000000}");
			content.append("</style>");
			content.append("<body>");
			content.append("<b>打卡成功</b><br /> ");

			CardList cardList = getRecord(user);
			List<Card> rows = cardList.getRows();
			// 没有记录
			if (rows != null && rows.size() > 0) {
				SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
				content.append("<table border='0' cellspacing='0' cellpadding='0'>");
				content.append("<tr>");
				content.append("<th>ID</th><th>工号</th><th>姓名</th><th>归属地</th><th>记录日期</th><th>有效范围（米）</th><th>开始时间</th><th>结束时间</th>");
				content.append("</tr>");
				for (Card card : rows) {
					content.append("<tr>");
					content.append("<td>").append(card.getId()).append("</td>");
					content.append("<td>").append(card.getLob_number()).append("</td>");
					content.append("<td>").append(card.getLast_name()).append("</td>");
					content.append("<td>").append(card.getOnwership_place()).append("</td>");
					content.append("<td>").append(sdf2.format(card.getRecord_date())).append("</td>");
					content.append("<td>").append(card.getShip_place_ind()).append("</td>");
					content.append("<td>").append(sdf1.format(card.getBegin_time())).append("</td>");
					content.append("<td>").append(sdf1.format(card.getEnd_time())).append("</td>");
					content.append("</tr>");
				}
				content.append("</table>");
				content.append("</body>");
			}
			content.append("</html>");
			mail.sendHtmlMail(user.getEmail(), "自动打卡", content.toString());
		} else {
			if (user.getPhone() != null && user.getPhone().trim().length() > 0) {
				Twilio.init(Constant.ACCOUNT_SID, Constant.AUTH_TOKEN);
				PhoneNumber to = new PhoneNumber("+86" + user.getPhone());
				PhoneNumber from = new PhoneNumber(Constant.PHONE_FROM);
				Message message = Message.creator(to, from, punshFailedContent).create();
				logger.info(user.getUserName() + " 已发送短信 " + message.getSid());
			} else {
				mail.sendSimpleMail(user.getEmail(), punshFailedTitle, punshFailedContent);
			}
		}
	}
	
	/**
	 * 正式执行程序-手动 不做任何其他操作，直接打卡
	 */
	public String execCustom() {
		try {
			// 第一步，初始化区域数据
			initAreaData();

			// 第二步，执行打卡，完全按微信流程执行
			getMylocation();
		} catch (IOException e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		} catch (InitAreaException e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		} catch (DistanceException e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " " + e.getMessage(), e);
			punshFailedContent += e.getMessage();
		}
		if (isSucced) {
			return "打卡成功";
		} else {
			return punshFailedContent;
		}
	}

	/**
	 * 判断是否已打卡
	 * 
	 * @param user
	 * @return
	 */
	private boolean checkPunshed(UserSession user) {
		Calendar c = Calendar.getInstance();
		CardList cardList = getRecord(user);
		List<Card> rows = cardList.getRows();
		// 没有记录
		if (rows == null || rows.size() < 1) {
			return false;
		}
		for (Card card : rows) {
			Calendar record_date = Calendar.getInstance();
			record_date.setTime(card.getRecord_date());
			// 获取当天的打卡记录
			if (record_date.get(Calendar.DAY_OF_MONTH) == c.get(Calendar.DAY_OF_MONTH)) {
				int currentTime = c.get(Calendar.HOUR_OF_DAY);
				// 判断当前时间是否上班打卡
				if (5 <= currentTime && 8 >= currentTime) {
					Date begin_time = card.getBegin_time();
					// 上班是否打卡
					if (begin_time != null) {
						Calendar temp = Calendar.getInstance();
						temp.setTime(begin_time);
						int begin_hour = temp.get(Calendar.HOUR_OF_DAY);
						// 上班打卡必须在5点到9点之间，必须做此判断，因为首次打卡时间在5点以前算作前一天的考勤
						if (5 <= begin_hour && 8 >= begin_hour) {
							return true;
						}
					}
				}

				// 判断当前时间是否下班打卡
				if (currentTime >= 18 || (currentTime >= 0 && currentTime < 5)) {
					Date last_update = card.getLast_update();
					// 下班是否打卡
					if (last_update != null) {
						Calendar temp = Calendar.getInstance();
						temp.setTime(last_update);
						int last_hour = temp.get(Calendar.HOUR_OF_DAY);
						// 下班打卡必须在18点到第二天5点之间，必须做此判断，因为末次打卡时间在18点以前算作早退
						if (last_hour >= 18 || (last_hour >= 0 && last_hour < 5)) {
							return true;
						}
					}
				}
				// 不满足以上任意条件
				return false;
			}
		}
		return false;
	}

	/**
	 * 初始化区域数据
	 * 
	 * @throws IOException
	 * @throws InitAreaException
	 */
	private void initAreaData() throws IOException, InitAreaException {
		String res = HttpUtil.sendPost(Constant.GET_AREA_LIST, null, Constant.encoding, user.getUserSessionId());
		ObjectMapper mapper = new ObjectMapper();
		JsonNode node = mapper.readTree(res);
		node = node.get("rows");
		this.areas = mapper.readValue(node.toString(), new TypeReference<List<Area>>() {
		});
		if (this.areas.size() < 1) {
			throw new InitAreaException(this.user.getUserName() + " 初始化区域数据异常");
		}
		//TODO 动态更新区域
//		areaService.setAreas(this.areas);
	}

	/**
	 * 执行打卡，完全按微信流程执行
	 * 
	 * @throws DistanceException
	 * 
	 * @throws Exception
	 */
	private void getMylocation() throws DistanceException {
		String log = "";
		// stage 1，初始日志
		log = addTimeTag(log, 1);
		// stage 2，判断微信是否支持js获取坐标接口
		log = addTimeTag(log, 2);
		// stage 3，获取坐标
		log = addTimeTag(log, 3);
		// stage 4，获取坐标成功
		log = addTimeTag(log, 4);
		// 执行打卡
		log = log + "  pushCardSucc " + displayLocation(1);
		log = addTimeTag(log, 5);
		addLog(log);
	}

	/**
	 * 执行打卡
	 * 
	 * @param latitude
	 * @param longitude
	 * @param ship_place_ind
	 * @param onwership_place
	 * @return
	 */
	private String getPunsh(double latitude, double longitude, int ship_place_ind, String onwership_place) {
		String result;
		Map<String, String> map = new HashMap<String, String>();
		map.put("latitude", latitude + "");
		map.put("longitude", longitude + "");
		map.put("ship_place_ind", ship_place_ind + "");
		map.put("onwership_place", onwership_place);
		map.put("isRemote", "0");
		result = HttpUtil.sendPost(Constant.POST_PUNSH, map, Constant.encoding, user.getUserSessionId());
		logger.info(user.getUserName() + " getPunsh 请求结果：" + result);
		if (result == null || "".equals(result) || '<' == result.charAt(0)) {
			logger.error(user.getUserName() + " 打卡失败");
		} else {
			this.isSucced = true;
		}
		return result;
	}

	/**
	 * 记录打卡日志
	 * 
	 * @param msg
	 */
	private void addLog(String msg) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("msg", "getLocation:ok  " + msg);
		map.put("locationval", this.latitude + "," + this.longitude);
		map.put("lobnumber", this.user.getUserAccount());
		map.put("name", this.user.getUserName());
		String res = HttpUtil.sendPost(Constant.ADD_RECORD_LOG, map, Constant.encoding, user.getUserSessionId());
		logger.info(user.getUserName() + " addLog 请求结果：" + res);
	}

	/**
	 * 获取打卡办公地点并打卡
	 * 
	 * @return
	 * @throws DistanceException
	 */
	private String displayLocation(int tryCnt) throws DistanceException {
		// 模拟获取当前位置
		Map<String, Double> randomAxes = getRandomAxes(user.getLatitude(), user.getLongitude());
		logger.info(user.getUserName() + " displayLocation 获取坐标：" + randomAxes + ", 本次为第" + tryCnt + "次获取");
		for (int i = 0; i < areas.size(); i++) {
			Area area = areas.get(i);
			double dis_temp = computeDistance(randomAxes, area);
			if (dis_temp < area.getShipPlaceInd()) {
				this.latitude = randomAxes.get("latitude");
				this.longitude = randomAxes.get("longitude");
				// 距离符合要求，执行打卡
				getPunsh(this.latitude, this.longitude, area.getShipPlaceInd(), area.getOnwership_place());
				return "1001" + area.getOffice();
			}
		}
		// 尝试10次失败抛异常
		if (tryCnt == 10) {
			throw new DistanceException(user.getUserName() + " 多次获取随机位置无效");
		}
		// 如果不符合所有条件，重新生成坐标
		return displayLocation(++tryCnt);
	}

	/**
	 * 根据坐标值计算到中心点的距离
	 * 
	 * @param randomAxes
	 * @param area
	 * @return
	 */
	private double computeDistance(Map<String, Double> randomAxes, Area area) {
		// 打卡位置经纬度转换为度
		double cur_lat_angle = degreesToRadians(randomAxes.get("latitude"));
		double cur_lob_angle = degreesToRadians(randomAxes.get("longitude"));
		// 中心点经纬度转换为度
		double center_lat_angle = 0;
		double center_lob_angle = 0;
		try{
			//区域列表中有异常数据
			center_lat_angle = degreesToRadians(Double.valueOf(area.getLatitude()));
			center_lob_angle = degreesToRadians(Double.valueOf(area.getLongitude()));
		}catch (Exception e) {
			e.printStackTrace();
			logger.error(user.getUserName() + " 区域列表异常数据>" + area);
		}
		
		return 6371000 * Math.acos(Math.sin(cur_lat_angle) * Math.sin(center_lat_angle)
				+ Math.cos(cur_lat_angle) * Math.cos(center_lat_angle) * Math.cos(cur_lob_angle - center_lob_angle));
	}

	/**
	 * 将角度转换为弧度
	 * 
	 * @param axe
	 * @return
	 */
	private double degreesToRadians(Double axe) {
		return axe * Math.PI / 180;
	}

	/**
	 * 生成时间标记
	 * 
	 * @param msg
	 * @param stage
	 * @return
	 */
	private String addTimeTag(String msg, int stage) {
		Date date = new Date();
		String localDate = new SimpleDateFormat("yyyy/M/dd ahh:mm:ss - SSS").format(date);
		return msg + "  " + stage + "  " + localDate;
	}

	/**
	 * 在指定地点附近生成随机坐标
	 * 
	 * @param lat
	 * @param lon
	 * @return
	 */
	private Map<String, Double> getRandomAxes(double lat, double lon) {
		Random random = new Random();

		// 取值范围[-150, 150] ,即random.nextInt(150 - (-150) + 1) + (-150)
		int a = random.nextInt(301) - 150;
		int b = random.nextInt(301) - 150;

		Map<String, Double> map = new HashMap<String, Double>();
		// 差之毫厘谬以千里，只在千分位之后随机
		map.put("latitude", Double.valueOf(String.format("%.5f", lat + a * 0.00001)));
		map.put("longitude", Double.valueOf(String.format("%.5f", lon + b * 0.00001)));
		return map;
	}

	/**
	 * 获取当月打卡记录
	 * 
	 * @return
	 */
	private CardList getRecord(UserSession user) {
		Map<String, String> map = new HashMap<String, String>();
		Calendar c = Calendar.getInstance();
		map.put("year", c.get(Calendar.YEAR) + "");
		map.put("month", (c.get(Calendar.MONTH) + 1) + "");
		String res = HttpUtil.sendPost(Constant.GET_CARD_LIST, map, Constant.encoding, user.getUserSessionId());
		ObjectMapper mapper = new ObjectMapper();
		mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S"));
		CardList cardList;
		try {
			cardList = mapper.readValue(res, CardList.class);
		} catch (Exception e) {
			cardList = new CardList();
			e.printStackTrace();
			logger.error(user.getUserName() + " 获取打卡记录失败", e);
		}
		return cardList;
	}
	
	public static void main(String[] args) {
		Date date = new Date();
		String localDate = new SimpleDateFormat("yyyy/M/dd ahh:mm:ss - SSS").format(date);
		System.out.println(localDate);
	}
}
